/**
 * 观察者模式：定义对象间的一种一对多的依赖关系，当一个对象的状态发生改变时，所有依赖于它的对象都要得到通知并自动更新。
 * 观察者模式适合应用场景:
 * 1. 当一个对象状态的改变需要改变其他对象，或实际对象是事先未知的或动态变化的时，可使用观察者模式。
 * 2. 当应用中的一些对象必须观察其他对象时，可使用该模式。但仅能在有限时间内或特定情况下使用。
 * 观察者模式从根本上讲必须包含两个角色：观察者和被观察对象。
 * 被观察对象自身应该包含一个容器来存放观察者对象，当被观察者自身发生改变时通知容器内所有的观察者对象自动更新。
 * 观察者对象可以注册到被观察者的中，完成注册后可以检测被观察者的变化，接收被观察者的通知。当然观察者也可以被注销掉，停止对被观察者的监控。
 */
#pragma once

#include <iostream>
#include <list>
#include <string>
#include <memory>

using namespace std;

namespace observer
{
class IObserver {
public:
    virtual ~IObserver() {};
    virtual void update(const string&) = 0;
};

class ISubject {
public:
    virtual void attach(IObserver* observer) = 0;
    virtual void detach(IObserver* observer) = 0;
    virtual void notify() = 0;
};

/**
 * Subject 拥有一些重要的状态，并在状态发生变化时通知观察者。
 */
class Subject : public ISubject {
public:
    virtual ~Subject() {
        cout << "Goodbye, I was the Subject." << endl;
    }

    // 订阅管理方法。
    void attach(IObserver*observer) override {
        m_listObserver.push_back(observer);
    }
    void detach(IObserver* observer) override {
        m_listObserver.remove(observer);
    }
    void notify() override {
        HowManyObserver();
        list<IObserver*>::iterator iter = m_listObserver.begin();
        while (iter != m_listObserver.end()) {
            (*iter)->update(m_message);
            ++iter;
        }
    }

    void CreateMessage(const string& message = "Empty") {
        m_message = message;
        notify();
    }
    void HowManyObserver() {
        cout << "There are " << m_listObserver.size() << " observers in the list." << endl;
    }

    // 通常情况下，订阅逻辑只是 Subject 真正能做的一小部分。Subject 通常持有一些重要的业务逻辑，每当有重要的事情即将发生（或发生后），就会触发一个通知方法。
    void SomeBusinessLogic() {
        m_message = "change message";
        notify();
        cout << "I'm about to do some thing important." << endl;
    }

private:
    list<IObserver*> m_listObserver;
    string m_message;
};

class Observer : public IObserver {
public:
    Observer(Subject& subject) : m_subject(subject) {
        m_subject.attach(this);
        std::cout << "Hi, I'm the Observer \"" << ++Observer::m_staticNumber << "\"." << endl;
        m_number = Observer::m_staticNumber;
    }
    virtual ~Observer() {
        cout << "Goodbye, I was the Observer \"" << this->m_number << "\"." << endl;
    }

    void update(const string &message_from_subject) override {
        m_message = message_from_subject;
        PrintInfo();
    }
    void RemoveMeFromTheList() {
        m_subject.detach(this);
        cout << "Observer \"" << m_number << "\" removed from the list." << endl;
    }
    void PrintInfo() {
        cout << "Observer \"" << this->m_number << "\": a new message is available --> " << this->m_message << endl;
    }

private:
    string m_message;
    Subject& m_subject;
    static int m_staticNumber;
    int m_number;
};

int Observer::m_staticNumber = 0;

void ClientCode() {
  unique_ptr<Subject> subject(new Subject);
  unique_ptr<Observer> observer1(new Observer(*subject));
  unique_ptr<Observer> observer2(new Observer(*subject));
  unique_ptr<Observer> observer3(new Observer(*subject));

  subject->CreateMessage("Hello World! :D");
  observer3->RemoveMeFromTheList();

  subject->CreateMessage("The weather is hot today! :p");
  unique_ptr<Observer> observer4(new Observer(*subject));

  observer2->RemoveMeFromTheList();
  unique_ptr<Observer> observer5(new Observer(*subject));

  subject->CreateMessage("My new car is great! ;)");
  observer5->RemoveMeFromTheList();

  observer4->RemoveMeFromTheList();
  observer1->RemoveMeFromTheList();
}
}   // namespace observer